package mr

import (
	"encoding/json"
	"errors"
	"os"
)

type MidFile struct {
	FileName string                 `json:"FileName"`
	KVsMap   map[string]KeyValueLis `json:"KVsMap"`
}

type KeyValueLis struct {
	Key       string   `json:"Key"`
	ValueList []string `json:"ValueList"`
}

// 中间文件写入文件系统
func WriteMidFile(mf *MidFile) error {
	MrLoger.Printf("Write MidFile:%v", mf.FileName)
	out, err := json.Marshal(mf)
	if err != nil {
		return err
	}
	// 打开文件
	file, err := os.OpenFile(mf.FileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		return err
	}
	defer file.Close()
	// 写入数据
	_, err = file.Write(out)
	if err != nil {
		return err
	}
	MrLoger.Printf("Write MidFile:%v - OK", mf.FileName)
	return nil
}

// 从中间文件读取数据
func ReadMidFile(filename string) (*MidFile, error) {
	// 打开文件
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	// 读取内容
	data, err := Read2Bytes(file)
	if err != nil {
		return nil, err
	}
	lenth := len(data)
	// fmt.Printf("midfile len == %v\n", lenth)
	if lenth == 0 {
		return nil, errors.New("FileRead len==0 cannot jsonUnmarshal")
	}
	// json解析
	mf := MidFile{}
	err = json.Unmarshal(data[:lenth], &mf)
	if err != nil {
		return nil, err
	}
	return &mf, nil
}

// 向文件中写入一个键值对
func (mf *MidFile) AddKv(kv *KeyValue) {
	if valis, ok := mf.KVsMap[kv.Key]; ok {
		valis.ValueList = append(valis.ValueList, kv.Value)
		mf.KVsMap[kv.Key] = valis
	} else {
		mf.KVsMap[kv.Key] = KeyValueLis{
			Key:       kv.Key,
			ValueList: []string{kv.Value},
		}
	}
}

func GenMidFile(filename string) MidFile {
	return MidFile{
		FileName: filename,
		KVsMap:   make(map[string]KeyValueLis),
	}
}

func GenKeyValueLis(key string) KeyValueLis {
	return KeyValueLis{
		Key:       key,
		ValueList: []string{},
	}
}

type MidFilePool struct {
	FileMap map[string]MidFile // map[filename]:MidFile
}

func GenMidFilePool() MidFilePool {
	return MidFilePool{
		FileMap: make(map[string]MidFile),
	}
}

// 文件池的文件写入到磁盘
func (mfp *MidFilePool) PoolWrite() error {
	for _, mf := range mfp.FileMap {
		err := WriteMidFile(&mf)
		if err != nil {
			return err
		}
	}
	return nil
}

// 向中间文件池中写入一个特定的键值对
func (mfp *MidFilePool) Add(filename string, kv *KeyValue) {
	if mf, ok := mfp.FileMap[filename]; ok {
		// MrLoger.Printf("MidFilePool: Kv[%v] TO File:%v", *kv, filename)
		mf.AddKv(kv)
	} else {
		MrLoger.Printf("MidFilePool: Add New File:%v", filename)
		mf := GenMidFile(filename)
		mf.AddKv(kv)
		mfp.FileMap[filename] = mf
	}
}
